package de.mmis.core.sexpression;

import java.io.IOException;
import java.io.InputStream;

/**
 * InputStream wrapper storing last read byte, so a single 'read' call can be
 * undone. <br>
 * Implements InputStream interface itself, so it can be used as normal
 * InputStream.
 * 
 * @author Christian Koch
 * 
 */
public class UndoInputStream extends InputStream {
	private InputStream in;

	private Integer lastReadChar;
	private boolean undone;

	/**
	 * creates new UndoInputStream to wrap given stream
	 * 
	 * @param in
	 *            InputStream to be wrapped
	 */
	public UndoInputStream(InputStream in) {
		this.in = in;
		this.lastReadChar = null;
		this.undone = false;
	}

	/**
	 * undo last 'read' call only one call can be undone, consecutive calls have
	 * no effect
	 * 
	 * @return last read character, or null, if no character read yet
	 */
	public Byte undo() {
		if (this.lastReadChar != null)
			this.undone = true;
		return new Byte(this.lastReadChar.byteValue());
	}

	/**
	 * closes underlying InputStream
	 * 
	 * @throws IOException
	 *             Exception of the underlying stream
	 */
	@Override
	public void close() throws IOException {
		this.in.close();
	}

	/**
	 * reads returns a given number of bytes
	 * 
	 * @param numberOfBytes
	 *            number of bytes to be read
	 * @return array of read bytes
	 * @throws IOException
	 *             Exception of the underlying stream
	 */
	public byte[] readBytes(int numberOfBytes) throws IOException {
		byte[] buf = new byte[numberOfBytes];
		this.read(buf);
		return buf;
	}

	/**
	 * reads single byte from underlying InputStream
	 * 
	 * @return byte read or -1, if end of stream reached
	 * @throws IOException
	 *             Exception of the underlying stream
	 */
	@Override
	public int read() throws IOException {
		if (this.undone) {
			this.undone = false;
			return this.lastReadChar.intValue();
		}

		int ret = this.in.read();
		if (ret >= 0)
			this.lastReadChar = new Integer(ret);
		return ret;
	}

	/**
	 * returns next byte in underlying InputStream without removing it from
	 * stream this is done by an 'read' call followed by an 'undo' call
	 * 
	 * @return next byte in underlying InputStream
	 * @throws IOException
	 *             Exception of the underlying stream
	 */
	public int peek() throws IOException {
		int ret = read();
		if (ret != -1)
			undo();
		return ret;
	}
}
